﻿#ifndef _SEMAPHORE_HPP_
#define _SEMAPHORE_HPP_

#include <assert.h>
#if defined(_WIN32)
#include <Windows.h>
#elif defined(__linux__)
#include <errno.h>
#include <semaphore.h>
#endif

#if defined(_WIN32)
typedef HANDLE	SemaphoreInternal;
#elif defined(__linux__)
typedef  sem_t	SemaphoreInternal;
#endif

class Semaphore
{
public:
	explicit Semaphore(int initialCount = 0)
	{
#if defined(_WIN32)
		Internal = CreateSemaphore(NULL,initialCount,max(initialCount,1),NULL);
		if (Internal == NULL)
		{
			perror("Semaphore Construct CreateSemaphore");
			throw errno;
		}
#elif defined(__linux__)
		memset(&Internal,0,sizeof(SemaphoreInternal));
		int r = sem_init(&Internal,0,initialCount);
		if (r != 0 )
		{
			perror("Semaphore Construct CreateSemaphore");
			throw errno;
		}
#endif
	}

	~Semaphore()
	{
#if defined(_WIN32)
		CloseHandle(Internal);
#elif defined(__linux__)
		sem_destroy(&Internal);
#endif
	}

	int wait()
	{
		int r = -1;
#if defined(_WIN32)
		if(WAIT_OBJECT_0 == WaitForSingleObject(Internal,INFINITE))
		{
			r = 0;
		}
#elif defined(__linux__)
		for(;;)
		{
			if(0 == sem_wait(&Internal))
			{
				r = 0;
			}
			else
			{
				if(errno == EINTR)
				{
					continue;
				}
				else
				{
					r = -1;
				}
			}

			break;
		}	

#endif
		return r;
	}

	int wait(unsigned int milliseconds)
	{
		int r = -1;
#if defined(_WIN32)
		if(WAIT_OBJECT_0 == WaitForSingleObject(Internal,milliseconds))
		{
			r = 0;
		}

#elif defined(__linux__)
		struct timespec ts;
		ts.tv_sec = time(NULL) + milliseconds/1000;
		ts.tv_nsec = (milliseconds%1000) * 1000 * 1000;
		
		for(;;)
		{
			if(0 == sem_timedwait(&Internal,&ts))
			{
				r = 0;
			}
			else
			{
				if(errno == EINTR)
				{
					continue;
				}
				else
				{
					r = -1;
				}
			}

			break;
		}	
#endif
		return r;
	}

	int post()
	{
		int r = -1;
#if defined(_WIN32)
		if (TRUE == ReleaseSemaphore(Internal, 1, NULL))
		{
			r = 0;
		}
#elif defined(__linux__)
		if(0 == sem_post(&Internal))
		{
			r = 0;
		}
#endif
		return r;
	}
private:
	SemaphoreInternal Internal;
};

#endif
